package com.zdp.leetcodeMiddle;


import java.util.Arrays;

/*
* 题目描述：
* 给定不同面额的硬币 coins 和一个总金额 amount。
* 编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
* 如果没有任何一种硬币组合能组成总金额，返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1：
输入：coins = [1, 2, 5], amount = 11
输出：3
解释：11 = 5 + 5 + 1
示例 2：
输入：coins = [2], amount = 3
输出：-1
示例 3：
输入：coins = [1], amount = 0
输出：0
示例 4：
输入：coins = [1], amount = 1
输出：1
示例 5：
输入：coins = [1], amount = 2
输出：2
提示：
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
* */
public class 零钱兑换_322 {
    private int result = Integer.MAX_VALUE;
    public static void main(String[] args) {
        零钱兑换_322 demo = new 零钱兑换_322();
        int[] coins = new int[] {
                2
        };
        int amount = 3;
        int i = demo.coinChange1(coins, amount);
        System.out.println(i);
    }
    /*
    * 暴力搜索+剪枝+回溯
    * 搜索所有的可能性，从最大的开始搜索，减少搜索的次数
    * 如果因为较大的金额的数量过多导致无法凑成总金额，就回溯，丢掉一个较大的金额
    * 如此往复。
    * */
    public int coinChange(int[] coins, int amount) {

        Arrays.sort(coins);
        f(coins,coins.length-1,amount,0);
        return result == Integer.MAX_VALUE? -1: result;
    }

    public void f(int[] coins,int index,int amount,int nums){
        if(amount == 0){
            result = Math.min(nums,result);
            return;
        }
        if(index==-1){
            // 说明没有找到 回溯
            return ;
        }
        // 进行搜查
        // 失败，回溯，减少数量
        for(int i=amount/coins[index];i>=0&&i+nums<result;i--){
            f(coins,index-1,amount-(coins[index]*i),nums+i);
        }
    }

    /*
    * 动态规划,自底向上
    * */
    public int coinChange1(int[] coins,int amount){
        int[] dp = new int[amount+1];
        // dp[i] 表示 组成i金额的最小数量
        // 假设 0~i-1都已经处理完毕
        // 则 dp[i] = （j为coins的索引0~n-1) min(dp[i-cj])+1
        Arrays.fill(dp,Integer.MAX_VALUE-1);
        dp[0] = 0;
        // 外层循环为金额
        // 内层则是 coins的循环
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.length;j++){
                if(coins[j]<=i){
                    // 硬币面值比金额小，才可以放进来
                    dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }

        // 若是没有解决方案，则 dp[amount] 应该=Integer.MAX_VALUE 或者 Intger.MAX_VALUE-1
        // 案例 [2] ,3
        return dp[amount]>=Integer.MAX_VALUE-1? -1:dp[amount];
    }

    /*
    * 动态规划，自顶向下
    * */
}
